﻿/*
 * Program.cs -- bGui Main File
 * 
 * New BSD License (BSD)
 * Copyright (c) 2012, Karim Alibhai
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;

// Include JSON Support
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace bGui
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(params string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(delegate(object sender, UnhandledExceptionEventArgs evt)
            {
                Exception ex = (Exception)evt.ExceptionObject;
                MessageBox.Show(ex.StackTrace, ex.Message);
                Environment.Exit(-1);
            });

            if (!(args.Length == 2 && args[0] == "init" && File.Exists(args[1]))) throw new Exception("Bad arguments");

            StreamReader arg = new StreamReader(File.OpenRead(args[1]));

            IDictionary vars = Environment.GetEnvironmentVariables();
            foreach(string val in vars.Keys)
                ArrayPush<bGuiVar>(ref bVars, new bGuiVar(val, vars[val].ToString()));

            while (arg.ReadLine().Trim().ToLower() != ":bgui_init") ; // entry_point
            Understand( arg );
        }

        // These will be handled like dynamic arrays, so set length = 1
        static bGuiForm[] bForms = new bGuiForm[1];
        static bGuiControl[] bControls = new bGuiControl[1];
        static bGuiVar[] bVars = new bGuiVar[1];

        static void Understand(StreamReader script)
        {
            while (!script.EndOfStream)
            {
                string line = ConvertVars(script.ReadLine().Trim());
                if (line != "")
                {
                    string cmd = line.Split(' ')[0];
                    string[] args = line.Replace(cmd + " ", "").Split(' ');
                    JObject json = JsonConvert.DeserializeObject<JObject>(line.Contains("{") ? line.Substring(line.IndexOf("{") - 1) : "{}");

                    switch (cmd.ToUpper())
                    {
                        // Refer to bGui as '@' or 'bgui'
                        case "BGUI": case "@":
                            switch (args[0].ToUpper())
                            {
                                case "-PROGRAM":
                                    switch (args[1].ToUpper())
                                    {
                                        case "ADDFORM":
                                            bGuiForm bNewForm = new bGuiForm(json["Name"].ToString(), new Form1());
                                            bNewForm.SetProperties(json);
                                            ArrayPush<bGuiForm>(ref bForms, bNewForm);
                                            break;

                                        case "NEWCTL":
                                            switch (json["Type"].ToString().ToUpper())
                                            {
                                                case "LABEL":
                                                    Label l = new Label();
                                                    bGuiLabel bL = new bGuiLabel(json["Name"].ToString(), l);
                                                    bL.SetProperties(json);
                                                    ArrayPush<bGuiControl>(ref bControls, (bGuiControl)bL);
                                                    break;

                                                default:
                                                    break;
                                            }
                                            break;

                                        default:
                                            break;
                                    }
                                    break;

                                case "-SET":
                                    ArrayPush<bGuiVar>(ref bVars, new bGuiVar(json["Var"].ToString(), json["Value"].ToString()));
                                    break;

                                case "-GOTO":
                                    // -GOTO only works with definitions below this point
                                    while ((line = script.ReadLine().Trim()) != ":" + json["Label"].ToString()) ;
                                    break;

                                default:
                                    for (int i = 0; i < bForms.Length; i++)
                                    {
                                        if (args[0].ToUpper() == ("-" + bForms[i].Name).ToUpper())
                                        {
                                            switch (args[1].ToUpper())
                                            {
                                                case "ADDCTL":
                                                    for (int n = 0; n < bControls.Length; n++)
                                                    {
                                                        if (bControls[n].Name.ToUpper() == json["Name"].ToString().ToUpper())
                                                        {
                                                            bForms[i].Append(bControls[n]);
                                                            break;
                                                        }
                                                    }
                                                    break;

                                                default:
                                                    try { if (json["Method"].ToString() != null) { bForms[i].Method(json); } }
                                                    catch { bForms[i].SetProperties(json); }
                                                    break;
                                            }
                                        }
                                    }
                                    break;
                            }
                            break;

                        default:
                            // Just batch it!
                            if (GetExecutable(cmd) != "echo.exe")
                            {
                                try
                                {
                                    Process.Start(new ProcessStartInfo()
                                    {
                                        FileName = (File.Exists(cmd) ? cmd : @"C:\WINDOWS\System32\" + GetExecutable(cmd)),
                                        WorkingDirectory = Environment.CurrentDirectory,
                                        UseShellExecute = false,
                                        CreateNoWindow = true,
                                    }).WaitForExit();
                                }
                                catch { }
                            }
                            break;
                    }
                }
            }
        }

        // Try allowing Windows to change values, as fast as possible
        static string ConvertVars(string line)
        {
            if (line.IndexOf("%") == -1) return line;

            Process p = Process.Start(new ProcessStartInfo()
            {
                FileName = @"C:\WINDOWS\System32\cmd.exe",
                Arguments = "/c echo " + line,
                UseShellExecute = false,
                CreateNoWindow = true,
                RedirectStandardOutput = true
            });

            for (int i = 0; i < bVars.Length; i++) if (line.ToUpper().Contains("%" + bVars[i].Var + "%")) p.StartInfo.EnvironmentVariables.Add(bVars[i].Var, bVars[i].Val);

            p.Start();
            p.WaitForExit();

            line = p.StandardOutput.ReadToEnd().Trim();

            return line;
        }

        // Using normal arrays as dynamic ones
        static void ArrayPush<t>(ref t[] tArray, t value)
        {
            for (int i = 0; i < tArray.Length; i++)
            {
                if (tArray[i] == null)
                {
                    tArray[i] = value;
                    return;
                }
            }

            Array.Resize<t>(ref tArray, tArray.Length + 1);
            tArray[tArray.Length - 1] = value;
        }

        // Get name of executable that batch file is calling
        static string GetExecutable(string cmd)
        {
            // This is actually a non-existent file
            string strRet = "echo.exe";

            cmd = cmd.ToLower();

            // Check file existension, where none exists
            if (File.Exists(cmd + ".exe")) strRet = cmd + ".exe";
            else if (File.Exists(cmd + ".com")) strRet = cmd + ".com";
            else if (File.Exists(cmd + ".bat")) strRet = cmd + ".bat";
            else if (File.Exists(cmd + ".cmd")) strRet = cmd + ".cmd";

            return strRet;
        }
    }
}
